/** !Reference counting atom with delete locking

This module contains code for adding reference counting and delete
locking to object by 'deriving' then from a base MX_ATOM object. */

#ifndef MX__DETK_ATOM_HEADER
#   define MX__DETK_ATOM_HEADER

#   include "deds/alloc.h"
#   include "deds/bool.h"
#   include <stdlib.h>

#   ifdef __cplusplus
extern "C" {
#   endif
#   ifdef MXDEBUG
#      define MX__ATOMNAME
#   else
#      undef MX__ATOMNAME
#   endif
	struct MX_ATOM;
	struct MX_LOCK;

	typedef void (*MX__ATOM_DESTRUCT) (void *atom);

/**reference_counting !Base class for reference counting and delete locking

MX_ATOM is an object intended to be used as a base class to
implement reference counting and delete locking in the derived class.
<p>
Properly constructed MX_ATOM objects are bit-copy-correct which means that
a binary copy retains proper functioning.  The copied object should not be
used after the binary copy.
<p>
Because MX_ATOM is bit-copy-correct it can safely be placed in a MX_VECTOR
or the memory realloc'd. */
	typedef struct MX_ATOM {
		MX__ATOM_DESTRUCT _destruct;
		struct MX_LOCK *_lastlock;
		unsigned char _flags;
#   ifdef MX__ATOMNAME
		const char *name;
#   endif
	} MX_ATOM;

/**type_conversion
!Convert to MX_ATOM
This macro return the base MX_ATOM for an object derived from MX_ATOM. */
#   define MXATOM(v) (&(v)->base.atom)

	void *mx__atom(MX_ATOM * atom, MX__ATOM_DESTRUCT destruct, size_t datasize);

/**reference_counting,func
!Construct an MX_ATOM
This macro constructs a MX_ATOM which is a base object for reference counting. */
#   define mx_atom(a,d,s)   mx__atom(MXATOM(a),(d),(s))

	unsigned mx__atom_delete(MX_ATOM * atom);

/**reference_counting,func
!Destroy an MX_ATOM
This macro causes an object to be marked for destruction.  If no locks (MX_LOCK)
or references (MX_REF) exist for this object then the object is destroyed and
if appropitae the memory is released. */
#   define mx_delete(a)     mx__atom_delete(MXATOM(a))

/**reference_counting,func
!Find out if an object is locked or not */
#   define mx_locked(a)     ((MXATOM(a)->_lastlock) ? (1) : (0))

#   ifdef MX__ATOMNAME
#      include <stdio.h>
#      define MXDEBUG_ATOMNAME_GET(a) ((const char *)(MXATOM(a)->name))
#      define MXDEBUG_ATOMNAME(a,n)   do { MXATOM(a)->name = (n); } while (0)

#   else
#      define MXDEBUG_ATOMNAME_GET(a) ((const char *)0)
#      define MXDEBUG_ATOMNAME(a,n)   do { } while (0)
#   endif

/**reference_counting
!Stop MX_ATOMs from getting deleted
This object implements a delete lock for an MX_ATOM.  While a valid 
MX_LOCK exists for an MX_ATOM object (or one derived from it) the
object is guaranteed not be deleted.  An object can have multiple locks
placed on it. <p>
MX_LOCK is NOT bit-copy-correct so it cannot safely be placed in a MX_VECTOR
or in memory that gets realloc'd. */
	typedef struct MX_LOCK {
		struct MX_ATOM *_atom;	/* Must be first so MX_REF works */
		struct MX_LOCK *_prev;
		struct MX_LOCK *_next;
	} MX_LOCK;

	void mx__lock(MX_LOCK * lock, struct MX_ATOM *atom);

/**reference_counting,func
!Construct a MX_LOCK
This macro creates a lock for an object derived from MX_ATOM.  The locked 
MX_ATOM is guaranteed not to be deleted while a valid lock exists. */
#   define mx_lock(l,a)     mx__lock((l),MXATOM(a))

/**reference_counting
!Destruct a MX_LOCK
This function destroys a MX_LOCK and removes it from it's associated
MX_ATOM object.  If the object has been flagged for deletion (with the
mx_delete() macro) then removal of the last lock caused the MX_ATOM
destructor to be called and (if necessary) memory returned to the operating
system.
<p>
If the release of the lock triggered calling of the object destructor then
the function returns non-zero.  If this occurs then it is not safe to access
the object anymore.  If this function returns false then either:<ol>
<li>Other locks exist for the object.</li>
<li>The object hasn't been deleted yet.</li>
</ol>In either case you can still safely access the object after the unlock. */
	unsigned mx_unlock(MX_LOCK * lock);

/**reference_counting,func
!Find out if a lock is the last one of an object */
	unsigned mx_lastlock(const MX_LOCK * lock);

/**reference_counting,type
!A reference type, a typed MX_LOCK
This macro defines a MX_LOCK where the target is of some defined type.
A reference functions the same way as a MX_LOCK except the locked (i.e. referenced)
object is available in a typesafe way.  A pointer to the reference target is
available with the MXREF macro.  */
#   define MX_REF(type) \
    union {                \
        type* _target;     \
        MX_LOCK _lock;     \
    }

/**reference_counting,func
!Construct a MX_REF
The macro constructs a MX_REF to an object. */
#   define mx_ref(r,t) mx__lock(&(r)->_lock, MXATOM(t))

/**reference_counting,func
!Destruct a MX_REF
The macro destroys a MX_REF to an object.  The object may
be deleted in the dereference, see the mx_unlock() macro. */
#   define mx_unref(r) mx_unlock(&(r)->_lock)

/**reference_counting,type_conversion
!Return the object behind a MX_REF
Return the object behind the MX_REF to an object. */
#   define MXREF(r) ((r)._target)

#   ifdef __cplusplus
}
#   endif
#endif
/*
   DEDS Data structures and utility functions for C
   Copyright (C) 2006 Douglas Eleveld

   This program is free software; you can redistribute
   it and/or modify it under the terms of the GNU
   General Public License as published by the Free
   Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will 
   be useful, but WITHOUT ANY WARRANTY; without even
   the implied warranty of MERCHANTABILITY or FITNESS
   FOR A PARTICULAR PURPOSE.  See the GNU General
   Public License for more details.

   You should have received a copy of the GNU General
   Public License along with this program; if not,
   write to the Free Software Foundation, Inc., 51
   Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 
 */
